package com.idea.relax.exception.core.manager;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;

/**
 * @author: 沉香
 * @date: 2023/5/26
 * @description: 接口实现类扫描器
 */
@SuppressWarnings({"all"})
public class InterfaceImplScanner {

	/**
	 * 扫描目标class和包名下的实现类
	 *
	 * @param target
	 * @param packageName
	 * @return
	 */
	public static List<Class> scanTargetSubClasses(Class target, Set<String> packageName) {
		ArrayList<Class> classList = new ArrayList<>();
		try {
			// 判断是否是一个接口
			if (target.isInterface()) {
				List<Class> allClass = getAllClass(packageName);
				//循环判断路径下的所有类是否实现了指定的接口 并且排除接口类自己
				for (Class cls : allClass) {
					//判断是不是同一个接口
					// isAssignableFrom:判定此 Class 对象所表示的类或接口与指定的 Class
					// 参数所表示的类或接口是否相同，或是否是其超类或超接口
					if (target.isAssignableFrom(cls)) {
						if (!target.equals(cls)) {
							// 自身并不加进去
							classList.add(cls);
						}
					}
				}
			} else {
				// 如果不是接口，则获取它的所有子类
				List<Class> allClass = getAllClass(target.getPackage().getName());
				//循环判断路径下的所有类是否继承了指定类 并且排除父类自己
				for (Class cls : allClass) {
					if (target.isAssignableFrom(cls)) {
						if (!target.equals(cls)) {
							// 自身并不加进去
							classList.add(cls);
						}
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("扫描[" + target.getName() + "]子类失败", e);
		}
		return classList;
	}

	/**
	 * 从一个指定路径下查找所有的类
	 */
	private static List<Class> getAllClass(String packageName) throws IOException, ClassNotFoundException {
		List<Class> list = new ArrayList<>();
		// 返回对当前正在执行的线程对象的引用。
		// 返回该线程的上下文 ClassLoader。
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		String path = packageName.replace('.', '/');
		List<File> fileList = new ArrayList<>();
		/**
		 * 这里面的路径使用的是相对路径 如果大家在测试的时候获取不到，请理清目前工程所在的路径 使用相对路径更加稳定！
		 * 另外，路径中切不可包含空格、特殊字符等！
		 */
		// getResources:查找所有给定名称的资源获取jar包中的实现类:
		Enumeration<URL> enumeration = classLoader.getResources(path);
		while (enumeration.hasMoreElements()) {
			URL url = enumeration.nextElement();
			// 获取此 URL 的文件名
			fileList.add(new File(url.getFile()));
		}
		for (File file : fileList) {
			list.addAll(findClass(file, packageName));
		}
		return list;
	}

	/**
	 * 从一个指定路径下查找所有的类
	 */
	private static List<Class> getAllClass(Set<String> packageNames) throws IOException, ClassNotFoundException {
		List<Class> list = new ArrayList<>();
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		List<File> fileList = new ArrayList<>();
		for (String packageName : packageNames) {
			String path = packageName.replace('.', '/');
			Enumeration<URL> enumeration = classLoader.getResources(path);
			while (enumeration.hasMoreElements()) {
				URL url = enumeration.nextElement();
				// 获取此 URL 的文件名
				fileList.add(new File(url.getFile()));
			}
			for (File file : fileList) {
				list.addAll(findClass(file, packageName));
			}
		}
		return list;
	}

	/**
	 * 如果file是文件夹，则递归调用findClass方法，或者文件夹下的类 如果file本身是类文件，则加入list中进行保存，并返回
	 *
	 * @param file
	 * @param packageName
	 * @return
	 */
	private static List<Class> findClass(File file, String packageName) throws ClassNotFoundException {
		List<Class> classList = new ArrayList<>();
		if (!file.exists()) {
			return classList;
		}
		// 返回一个抽象路径名数组，这些路径名表示此抽象路径名表示的目录中的文件。
		File[] files = file.listFiles();
		for (File f : files) {
			if (f.isDirectory()) {
				if (!f.getName().contains(".")) {
					List<Class> arrayList = findClass(f, packageName + "." + f.getName());
					classList.addAll(arrayList);
				}
			} else if (f.getName().endsWith(".class")) {
				// 保存的类文件不需要后缀.class
				classList.add(Class.forName(packageName + '.' + f.getName().substring(0, f.getName().length() - 6)));
			}
		}
		return classList;
	}


}
